﻿/*
**  BodyDef
*/
// A rigid body. Internal computation are done in terms
// of the center of mass position. The center of mass may
// be offset from the body's origin.
if (!$.Engine.Dynamic || typeof $.Engine.Dynamic === "undefined") $.Engine.Dynamic = {};
$.Engine.Dynamic.Body = function (bd, world) {
  // initialize instance variables for references
  this.sMat0 = new $.Engine.Math.Mat2D();
  this.m_position = new $.Engine.Math.Vector2D();
  this.m_R = new $.Engine.Math.Mat2D(0);
  this.m_position0 = new $.Engine.Math.Vector2D();
  //

  var i = 0;
  var sd;
  var massData;

  this.m_flags = 0;
  this.m_position.SetV(bd.position);
  this.m_rotation = bd.rotation;
  this.m_R.Set(this.m_rotation);
  this.m_position0.SetV(this.m_position);
  this.m_rotation0 = this.m_rotation;
  this.m_world = world;

  this.m_linearDamping = $.Engine.Math.Clamp(1.0 - bd.linearDamping, 0.0, 1.0);
  this.m_angularDamping = $.Engine.Math.Clamp(1.0 - bd.angularDamping, 0.0, 1.0);

  this.m_force = new $.Engine.Math.Vector2D(0.0, 0.0);
  this.m_torque = 0.0;

  this.m_mass = 0.0;

  var massDatas = new Array($.Engine.Settings.maxShapesPerBody);
  for (i = 0; i < $.Engine.Settings.maxShapesPerBody; i++) {
    massDatas[i] = new $.Engine.Collision.Shape.MassData();
  }

  // Compute the shape mass properties, the bodies total mass and COM.
  this.m_shapeCount = 0;
  this.m_center = new $.Engine.Math.Vector2D(0.0, 0.0);
  for (i = 0; i < $.Engine.Settings.maxShapesPerBody; ++i) {
    sd = bd.shapes[i];
    if (sd == null) break;
    massData = massDatas[i];
    sd.ComputeMass(massData);
    this.m_mass += massData.mass;
    //this.m_center += massData->mass * (sd->localPosition + massData->center);
    this.m_center.x += massData.mass * (sd.localPosition.x + massData.center.x);
    this.m_center.y += massData.mass * (sd.localPosition.y + massData.center.y);
    ++this.m_shapeCount;
  }

  // Compute center of mass, and shift the origin to the COM.
  if (this.m_mass > 0.0) {
    this.m_center.Multiply(1.0 / this.m_mass);
    this.m_position.Add($.Engine.Math.MulMV(this.m_R, this.m_center));
  }
  else {
    this.m_flags |= $.Engine.Dynamic.Body.e_staticFlag;
  }

  // Compute the moment of inertia.
  this.m_I = 0.0;
  for (i = 0; i < this.m_shapeCount; ++i) {
    sd = bd.shapes[i];
    massData = massDatas[i];
    this.m_I += massData.I;
    var r = $.Engine.Math.SubtractVV($.Engine.Math.AddVV(sd.localPosition, massData.center), this.m_center);
    this.m_I += massData.mass * $.Engine.Math.Dot(r, r);
  }

  if (this.m_mass > 0.0) {
    this.m_invMass = 1.0 / this.m_mass;
  }
  else {
    this.m_invMass = 0.0;
  }

  if (this.m_I > 0.0 && bd.preventRotation == false) {
    this.m_invI = 1.0 / this.m_I;
  }
  else {
    this.m_I = 0.0;
    this.m_invI = 0.0;
  }

  // Compute the center of mass velocity.
  this.m_linearVelocity = $.Engine.Math.AddVV(bd.linearVelocity, $.Engine.Math.CrossFV(bd.angularVelocity, this.m_center));
  this.m_angularVelocity = bd.angularVelocity;

  this.m_jointList = null;
  this.m_contactList = null;
  this.m_prev = null;
  this.m_next = null;

  // Create the shapes.
  this.m_shapeList = null;
  for (i = 0; i < this.m_shapeCount; ++i) {
    sd = bd.shapes[i];
    var shape = $.Engine.Collision.Shape.Shape.Create(sd, this, this.m_center);
    shape.m_next = this.m_shapeList;
    this.m_shapeList = shape;
  }

  this.m_sleepTime = 0.0;
  if (bd.allowSleep) {
    this.m_flags |= $.Engine.Dynamic.Body.e_allowSleepFlag;
  }
  if (bd.isSleeping) {
    this.m_flags |= $.Engine.Dynamic.Body.e_sleepFlag;
  }

  if ((this.m_flags & $.Engine.Dynamic.Body.e_sleepFlag) || this.m_invMass == 0.0) {
    this.m_linearVelocity.Set(0.0, 0.0);
    this.m_angularVelocity = 0.0;
  }

  this.m_userData = bd.userData;
};

Object.extend($.Engine.Dynamic.Body.prototype, {
  // Set the position of the body's origin and rotation (radians).
  // This breaks any contacts and wakes the other bodies.
  SetOriginPosition: function (position, rotation) {
    if (this.IsFrozen()) {
      return;
    }

    this.m_rotation = rotation;
    this.m_R.Set(this.m_rotation);
    this.m_position = $.Engine.Math.AddVV(position, $.Engine.Math.b2MulMV(this.m_R, this.m_center));

    this.m_position0.SetV(this.m_position);
    this.m_rotation0 = this.m_rotation;

    for (var s = this.m_shapeList; s != null; s = s.m_next) {
      s.Synchronize(this.m_position, this.m_R, this.m_position, this.m_R);
    }

    this.m_world.m_broadPhase.Commit();
  },

  // Get the position of the body's origin. The body's origin does not
  // necessarily coincide with the center of mass. It depends on how the
  // shapes are created.
  GetOriginPosition: function () {
    return $.Engine.Math.SubtractVV(this.m_position, $.Engine.Math.b2MulMV(this.m_R, this.m_center));
  },

  // Set the position of the body's center of mass and rotation (radians).
  // This breaks any contacts and wakes the other bodies.
  SetCenterPosition: function (position, rotation) {
    if (this.IsFrozen()) {
      return;
    }

    this.m_rotation = rotation;
    this.m_R.Set(this.m_rotation);
    this.m_position.SetV(position);

    this.m_position0.SetV(this.m_position);
    this.m_rotation0 = this.m_rotation;

    for (var s = this.m_shapeList; s != null; s = s.m_next) {
      s.Synchronize(this.m_position, this.m_R, this.m_position, this.m_R);
    }

    this.m_world.m_broadPhase.Commit();
  },

  // Get the position of the body's center of mass. The body's center of mass
  // does not necessarily coincide with the body's origin. It depends on how the
  // shapes are created.
  GetCenterPosition: function () {
    return this.m_position;
  },

  // Get the rotation in radians.
  GetRotation: function () {
    return this.m_rotation;
  },

  GetRotationMatrix: function () {
    return this.m_R;
  },

  // Set/Get the linear velocity of the center of mass.
  SetLinearVelocity: function (v) {
    this.m_linearVelocity.SetV(v);
  },
  GetLinearVelocity: function () {
    return this.m_linearVelocity;
  },

  // Set/Get the angular velocity.
  SetAngularVelocity: function (w) {
    this.m_angularVelocity = w;
  },
  GetAngularVelocity: function () {
    return this.m_angularVelocity;
  },

  // Apply a force at a world point. Additive.
  ApplyForce: function (force, point) {
    if (this.IsSleeping() == false) {
      this.m_force.Add(force);
      this.m_torque += $.Engine.Math.b2CrossVV($.Engine.Math.SubtractVV(point, this.m_position), force);
    }
  },

  // Apply a torque. Additive.
  ApplyTorque: function (torque) {
    if (this.IsSleeping() == false) {
      this.m_torque += torque;
    }
  },

  // Apply an impulse at a point. This immediately modifies the velocity.
  ApplyImpulse: function (impulse, point) {
    if (this.IsSleeping() == false) {
      this.m_linearVelocity.Add($.Engine.Math.MulFV(this.m_invMass, impulse));
      this.m_angularVelocity += (this.m_invI * $.Engine.Math.b2CrossVV($.Engine.Math.SubtractVV(point, this.m_position), impulse));
    }
  },

  GetMass: function () {
    return this.m_mass;
  },

  GetInertia: function () {
    return this.m_I;
  },

  // Get the world coordinates of a point give the local coordinates
  // relative to the body's center of mass.
  GetWorldPoint: function (localPoint) {
    return $.Engine.Math.AddVV(this.m_position, $.Engine.Math.b2MulMV(this.m_R, localPoint));
  },

  // Get the world coordinates of a vector given the local coordinates.
  GetWorldVector: function (localVector) {
    return $.Engine.Math.b2MulMV(this.m_R, localVector);
  },

  // Returns a local point relative to the center of mass given a world point.
  GetLocalPoint: function (worldPoint) {
    return $.Engine.Math.b2MulTMV(this.m_R, $.Engine.Math.SubtractVV(worldPoint, this.m_position));
  },

  // Returns a local vector given a world vector.
  GetLocalVector: function (worldVector) {
    return $.Engine.Math.b2MulTMV(this.m_R, worldVector);
  },

  // Is this body static (immovable)?
  IsStatic: function () {
    return (this.m_flags & $.Engine.Dynamic.Body.e_staticFlag) == $.Engine.Dynamic.Body.e_staticFlag;
  },

  IsFrozen: function () {
    return (this.m_flags & $.Engine.Dynamic.Body.e_frozenFlag) == $.Engine.Dynamic.Body.e_frozenFlag;
  },

  // Is this body sleeping (not simulating).
  IsSleeping: function () {
    return (this.m_flags & $.Engine.Dynamic.Body.e_sleepFlag) == $.Engine.Dynamic.Body.e_sleepFlag;
  },

  // You can disable sleeping on this particular body.
  AllowSleeping: function (flag) {
    if (flag) {
      this.m_flags |= $.Engine.Dynamic.Body.e_allowSleepFlag;
    }
    else {
      this.m_flags &= ~$.Engine.Dynamic.Body.e_allowSleepFlag;
      this.WakeUp();
    }
  },

  // Wake up this body so it will begin simulating.
  WakeUp: function () {
    this.m_flags &= ~$.Engine.Dynamic.Body.e_sleepFlag;
    this.m_sleepTime = 0.0;
  },

  // Get the list of all shapes attached to this body.
  GetShapeList: function () {
    return this.m_shapeList;
  },

  GetContactList: function () {
    return this.m_contactList;
  },

  GetJointList: function () {
    return this.m_jointList;
  },

  // Get the next body in the world's body list.
  GetNext: function () {
    return this.m_next;
  },

  GetUserData: function () {
    return this.m_userData;
  },

  Destroy: function () {
    var s = this.m_shapeList;
    while (s) {
      var s0 = s;
      s = s.m_next;

      $.Engine.Collision.Shape.Shape.Destroy(s0);
    }
  },

  // Temp mat
  sMat0: new $.Engine.Math.Mat2D(),
  SynchronizeShapes: function () {
    //b2Mat22 R0(this.m_rotation0);
    this.sMat0.Set(this.m_rotation0);
    for (var s = this.m_shapeList; s != null; s = s.m_next) {
      s.Synchronize(this.m_position0, this.sMat0, this.m_position, this.m_R);
    }
  },

  QuickSyncShapes: function () {
    for (var s = this.m_shapeList; s != null; s = s.m_next) {
      s.QuickSync(this.m_position, this.m_R);
    }
  },

  // This is used to prevent connected bodies from colliding.
  // It may lie, depending on the collideConnected flag.
  IsConnected: function (other) {
    for (var jn = this.m_jointList; jn != null; jn = jn.next) {
      if (jn.other == other)
        return jn.joint.m_collideConnected == false;
    }

    return false;
  },

  Freeze: function () {
    this.m_flags |= this.e_frozenFlag;
    this.m_linearVelocity.Set(0.0, 0.0);
    this.m_angularVelocity = 0.0;

    for (var s = this.m_shapeList; s != null; s = s.m_next) {
      s.DestroyProxy();
    }
  },

  m_flags: 0,

  m_position: new $.Engine.Math.Vector2D(),
  m_rotation: null,
  m_R: new $.Engine.Math.Mat2D(0),

  // Conservative advancement data.
  m_position0: new $.Engine.Math.Vector2D(),
  m_rotation0: null,

  m_linearVelocity: null,
  m_angularVelocity: null,

  m_force: null,
  m_torque: null,

  m_center: null,

  m_world: null,
  m_prev: null,
  m_next: null,

  m_shapeList: null,
  m_shapeCount: 0,

  m_jointList: null,
  m_contactList: null,

  m_mass: null,
  m_invMass: null,
  m_I: null,
  m_invI: null,

  m_linearDamping: null,
  m_angularDamping: null,

  m_sleepTime: null,

  m_userData: null  
});
$.Engine.Dynamic.Body.e_staticFlag = 0x0001;
$.Engine.Dynamic.Body.e_frozenFlag = 0x0002;
$.Engine.Dynamic.Body.e_islandFlag = 0x0004;
$.Engine.Dynamic.Body.e_sleepFlag = 0x0008;
$.Engine.Dynamic.Body.e_allowSleepFlag = 0x0010;
$.Engine.Dynamic.Body.e_destroyFlag = 0x0020;